package com.fitns.user.service.user;

import com.fitns.user.constant.Constants;
import com.fitns.user.constant.RedisKey;
import com.fitns.user.dao.user.PrivilegeDao;
import com.fitns.user.dao.user.RolePrivilegeDao;
import com.fitns.user.dao.user.UrlDao;
import com.fitns.user.dto.user.PrivilegeVO;
import com.fitns.user.dto.user.RoleVO;
import com.fitns.user.dto.user.UserVO;
import com.fitns.user.exception.PrivilegeMsgCode;
import com.fitns.user.exception.ServiceException;
import com.fitns.user.model.user.Privilege;
import com.fitns.user.model.user.RolePrivilege;
import com.fitns.user.model.user.Url;
import com.fitns.user.utils.ContextUtils;
import com.fitns.user.utils.RedisUtil;
import com.google.common.collect.Lists;
import com.xiaoleilu.hutool.bean.BeanUtil;
import com.xiaoleilu.hutool.lang.Validator;
import com.xiaoleilu.hutool.util.CollectionUtil;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;

import java.util.*;
import java.util.stream.Collectors;


/**
 * Created by zhou  on 2018/08/02
 */

@Service
public class PrivilegeService {

    @Autowired
    private PrivilegeDao privilegeDao;

    @Autowired
    private UserService userService;

    @Autowired
    private UrlDao urlDao;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private RolePrivilegeDao rolePrivilegeDao;

    @Transactional
    public PrivilegeVO addPrivilege(PrivilegeVO privilegeVO) {
        if (Validator.isNull(privilegeVO)) {
            return null;
        }
        validatePrivilegeForAdd(privilegeVO);
        Privilege privilege = new Privilege();
        BeanUtil.copyProperties(privilegeVO, privilege);

        privilegeDao.insert(privilege);
        privilegeVO.setId(privilege.getId());
        processPrivilegeUrl(privilegeVO);
        if (Validator.isNotEmpty(privilegeVO.getSubPrivilege())) {
            for (PrivilegeVO vo : privilegeVO.getSubPrivilege()) {
                this.addPrivilege(vo);
            }
        }
        return privilegeVO;

    }

    private void validatePrivilegeForAdd(Privilege privilege) {
        privilege.setId(null);
        validateFields(privilege);
    }

    private void validateFields(Privilege privilege) {
        if (Validator.isEmpty(privilege.getCode())) {
            throw ServiceException.create(PrivilegeMsgCode.PRIVILEGE_CODE_IS_NULL);
        }
        if (privilege.getGrade() > Constants.PrivilegeGrade.TOP_GRADE && Validator.isNull(privilege.getParentId())) {
            throw ServiceException.create(PrivilegeMsgCode.PRIVILEGE_GRADE_PARENT);
        }
    }

    public void processPrivilegeUrl(PrivilegeVO privilegeVO) {

    }

    private Url findUrlByCode(String code) {
        Url url = urlDao.selectOne(Url.builder().code(code).build());
        return url;
    }

    public List<Url> refreshUrls(Map<RequestMappingInfo, HandlerMethod> map, String contextPath) {
        List<Url> urls = Lists.newArrayList();
        for (RequestMappingInfo info : map.keySet()) {
            PatternsRequestCondition urlCondition = info.getPatternsCondition();
            RequestMethodsRequestCondition methodCodition = info.getMethodsCondition();
            HandlerMethod handlerMethod = map.get(info);
            ApiOperation apiOperation = handlerMethod.getMethodAnnotation(ApiOperation.class);
            String name = "";
            if (Validator.isNotNull(apiOperation)) {
                name = apiOperation.value();
            }
            for (String pattern : urlCondition.getPatterns()) {
                for (RequestMethod method : methodCodition.getMethods()) {
                    String code = ContextUtils.generateUrlCode(contextPath + pattern, method);
                    Url url = this.findUrlByCode(code);
                    if (Validator.isNotNull(url)) {
                        url.setName(name);
                        urlDao.updateById(url);
                        continue;
                    }
                    url = new Url();
                    url.setCode(code);
                    url.setName(name);
                    url.setUrl(pattern);
                    url.setMethod(method);
                    urlDao.insert(url);
                    urls.add(url);
                }
            }
        }
        return urls;
    }


    public PrivilegeVO add(PrivilegeVO privilegeVo) {
        validatePrivilegeForAdd(privilegeVo);
        Privilege privilege = new Privilege();
        BeanUtil.copyProperties(privilegeVo, privilege);

        privilegeDao.insert(privilege);
        privilegeVo.setId(privilege.getId());
        processPrivilegeUrl(privilegeVo);
        if (Validator.isNotEmpty(privilegeVo.getSubPrivilege())) {
            for (PrivilegeVO vo : privilegeVo.getSubPrivilege()) {
                this.add(vo);
            }
        }
        return privilegeVo;
    }

    /**
     * 初始化可以访问URL对应的角色映射列表
     */
    public synchronized void initUrlCodeRoleIdMapping() {
        List<Url> urls = urlDao.selectUrlCodeMapRoleId();
        Map<String, Set<Long>> urlCodeRoleIdsMap = new HashMap<>();
        for (Url url : urls) {
            Set<Long> roleIds = urlCodeRoleIdsMap.get(url.getCode());
            if (CollectionUtil.isEmpty(roleIds)) {
                roleIds = new HashSet<>();
                urlCodeRoleIdsMap.put(url.getCode(), roleIds);
            }
            roleIds.add(url.getId());
        }
        for (Map.Entry<String, Set<Long>> entry : urlCodeRoleIdsMap.entrySet()) {
            redisUtil.setex(RedisKey.URL_CODE_MAP_ROLE_IDS.of(entry.getKey()), entry.getValue(), Integer.MAX_VALUE);
        }
        // redisUtil.delete(RedisKey.PRIVILEGE.of(RedisKey.PRIVILEGE.name()));
    }

    public List<PrivilegeVO> getPrivilegeTree(UserVO userVo) {

        List<Privilege> privileges = getAllPrivileges();

        List<Privilege> userPrivilege = Collections.emptyList();
        if (Validator.isNotNull(userVo)) {
            userPrivilege = this.getPrivileges(userVo);
        }
        List<PrivilegeVO> priviligeVoTree = buildPrivilegeTree(privileges, userPrivilege);
        return priviligeVoTree;
    }
    /**
     *
     * 功能描述: 构建权限树
     *
     * @param:
     * @return:
     */
    private List<PrivilegeVO> buildPrivilegeTree(List<Privilege> privileges, List<Privilege> userPrivileges) {
        Set<Long> userPrivilegeIds = userPrivileges.parallelStream().map(Privilege::getId).collect(Collectors.toSet());
        // 如果用户拥有权限则checked=true
        List<PrivilegeVO> privilegeVos = privileges.parallelStream().map(privilege -> {
            PrivilegeVO privilegeVo = new PrivilegeVO();
            BeanUtil.copyProperties(privilege, privilegeVo);
            if (userPrivilegeIds.contains(privilege.getId())) {
                privilegeVo.setChecked(true);
            }
            return privilegeVo;
        }).collect(Collectors.toList());
        Map<Long, PrivilegeVO> tmpMap = new HashMap<Long, PrivilegeVO>();
        // 将List转成树
        for (PrivilegeVO privilegeVo : privilegeVos) {
            PrivilegeVO tmpPrivilegeVo = tmpMap.get(privilegeVo.getId());
            if (Validator.isNull(tmpPrivilegeVo)) {
                tmpMap.put(privilegeVo.getId(), privilegeVo);
                privilegeVo.setSubPrivilege(Lists.newArrayList());
            }
            if (Validator.isNotNull(privilegeVo.getParentId())) {
                PrivilegeVO parentPrivilegeVo = tmpMap.get(privilegeVo.getParentId());
                if (Validator.isNotNull(parentPrivilegeVo)) {
                    parentPrivilegeVo.getSubPrivilege().add(privilegeVo);
                }
            }
        }

        List<PrivilegeVO> privilegeVoList = privilegeVos.parallelStream()
                .filter(privilege -> privilege.getGrade() == Constants.PrivilegeGrade.TOP_GRADE).collect(Collectors.toList());
        return privilegeVoList;
    }

    /**
     *
     * 功能描述: 查出所有权限
     *
     * @param:
     * @return:
     */
    private List<Privilege> getAllPrivileges() {
        @SuppressWarnings("unchecked")
        List<Privilege> privileges = new ArrayList<>();
        privileges = privilegeDao.selectAll();
        // 权限按等级排序
        privileges.sort(new Comparator<Privilege>() {
            @Override
            public int compare(Privilege o1, Privilege o2) {
                if (o1.getGrade() == o2.getGrade()) {
                    return o1.getOrderNum() - o2.getOrderNum();
                }
                return o1.getGrade() - o2.getGrade();
            }
        });
        // redisUtil.setex(RedisKey.PRIVILEGE.of(RedisKey.PRIVILEGE.name()), privileges, Integer.MAX_VALUE);
        return privileges;
    }

    /**
     *
     * 功能描述: 查找当前登录用户的所有权限
     *
     * @param:
     * @return:
     */

    public List<Privilege> getCurrentUserPrivilege(){
        UserVO userVO= userService.findDetailUser(userService.currentUser().getId());

        List<Privilege> privileges= this.getPrivileges(userVO);
        privileges.sort(new Comparator<Privilege>() {
            @Override
            public int compare(Privilege o1, Privilege o2) {
                if (o1.getGrade() == o2.getGrade()) {
                    return o1.getOrderNum() - o2.getOrderNum();
                }
                return o1.getGrade() - o2.getGrade();
            }
        });

        return privileges;
    }


    public List<Privilege> findPrivilegeByUser(RoleVO roleVO){
        if(Validator.isNull(roleVO)){
            return null;
        }
        RolePrivilege rolePrivilege=new RolePrivilege();
        rolePrivilege.setRoleId(roleVO.getId());
        List<RolePrivilege> rolePrivilegeList=rolePrivilegeDao.search(rolePrivilege);
        if (Validator.isNotNull(rolePrivilegeList) && rolePrivilegeList.size() != 0) {
            List<Long> privilegeIds = rolePrivilegeList.parallelStream().
                    map(RolePrivilege::getPrivilegeId).collect(Collectors.toList());

            List<Privilege> privilegeList = privilegeDao.seletBatchs(privilegeIds);
            return privilegeList;
        }
        return null;
    }

    /**
     *
     * 功能描述: 查找用户的权限列表
     *
     * @param:
     */
    public List<Privilege> getPrivileges(UserVO userVo) {
        Set<RolePrivilege> rolePrivilegeSet = new HashSet<>();
        if (CollectionUtil.isEmpty(userVo.getRoleVOS())) {
            return Collections.emptyList();
        }
        userVo.getRoleVOS().forEach(roleVo -> {
           RolePrivilege rolePrivilege=new RolePrivilege();
           rolePrivilege.setRoleId(roleVo.getId());
            List<RolePrivilege> rolePrivileges =rolePrivilegeDao.search(rolePrivilege);
            rolePrivilegeSet.addAll(rolePrivileges);
        });
        List<Long> privilegeIds = rolePrivilegeSet.parallelStream().map(RolePrivilege::getPrivilegeId)
                .collect(Collectors.toList());
        List<Privilege> privileges = Collections.emptyList();
        if (CollectionUtil.isNotEmpty(privilegeIds)) {
            privileges = privilegeDao.seletBatchs(privilegeIds);
        }
        // privileges = setSubPrivilege(privileges);
        return privileges;
    }
}
